# -*- coding: utf-8 -*-
# standard
import os
import sys

# third
from sqlalchemy import Index

# local
_P_PATH =  os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
if _P_PATH not in sys.path:
    sys.path.append(_P_PATH)
from models.base_model import *


"""
@Title:  微服务事件 模型 
@File: event_model.py
@Author: walle 2025年01日05日 12时34分55秒
@Version: 1.0.0
@Desc: 
"""


BEAT_INTERVAL = 2  # 周期性任务的执行间隔 单位分钟



class SensorEventType(MyEnum):
    """
    温度传感器事件类型
    这个模型和 iot-collector 共用
    """
    THRESHOLD: str = "超过阈值"
    OFFLINE: str = "设备离线"


class EventStatus(MyEnum):
    # 定义一个枚举类，表示事件的状态
    OK: int = 0  # 定义一个常量，表示事件状态为已恢复
    ERR: int = 1 # 定义一个常量，表示事件状态为错误


class EventType(MyEnum):
    """
    微服务事件类型
    """
    IOT: str = "物联网"
    GATEWAY: str = "网关"
    LAN: str = "内网"


class RawObjectStatus(MyEnum):
    OK: int = 0  # 正常
    PROBLEM: int = 1  # 有问题


class MicServiceEventOrm(Base, OrmTools):
    __tablename__ = 'mic_service_event'
    __table_args__ = (
        {"comment": "微服务事件"}
    )
    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键")
    customer_id = Column(Integer, nullable=True, default=None, comment="品牌客户id")
    customer_name = Column(String(255), nullable=True, default=None, comment="品牌客户名称")
    store_id = Column(Integer, nullable=True, default=None, comment="门店id")
    store_name = Column(String(128), nullable=True, default=None, comment="门店名称")
    event_type = Column(String(64), nullable=True, default=None, comment="事件类型 鉴别器 EventType")
    message = Column(String(256), nullable=True, default=None, comment="消息内容")
    is_alert = Column(Integer, nullable=True, default=1, comment="是告警还是预警 1:告警 0:预警 将来可能被 priority 替代")
    priority = Column(String(31), nullable=True, default=None, comment="事件优先级 EventPriority")
    begin_value = Column(String(31), nullable=True, default=None, comment="事件开始时值")
    end_value = Column(String(31), nullable=True, default=None, comment="事件结束时值")
    alert_time = Column(DateTime, nullable=True, default=None, comment="事件发生时间")
    recovery_time = Column(DateTime, nullable=True, default=None, comment="事件恢复时间")
    push_time = Column(DateTime, nullable=True, default=None, comment="微服务端推送的事件的时间")
    create_time = Column(DateTime, nullable=True, default=datetime.now, comment="创建时间")
    update_time = Column(DateTime, nullable=True, default=datetime.now, onupdate=datetime.now, comment="更新时间")
    
    # 定义一个鉴别器列，用于区分不同的事件类型
    __mapper_args__ = {
        'polymorphic_identity':'微服务事件',
        'polymorphic_on':event_type
    }
    # 定义一个join优化查询的索引, 用于和子类的索引配合优化查询速度
    # 注意 id放在了第一位，这是为了和子表的索引的最后一个列衔接，从而优化查询速度
    # create index ix_mic_service_event_quick_query on mic_service_event (id, store_id, alert_time desc);
    Index("ix_mic_service_event_quick_query", 
          "id", "store_id", 'alert_time',
          postgresql_ops={"alert_time": "DESC"},
          )

    @classmethod
    def delete(cls, ids: List[int], customer_id: int | None = None, db_session: Session | None = None) -> Resp:
        """
        删除
        """
        resp = Resp()
        db_session, need_close = (new_db_session(sync=True), True) if db_session is None else (db_session, False)
        s = select(cls).where(cls.id.in_(ids))
        for x in db_session.scalars(s):
            if x.customer_id != customer_id:
                resp.message = f"权限不足"
                break
        if resp:
            db_session.execute(delete(cls).where(cls.id.in_(ids)))
            if cls.__tablename__ != "notification_method": # 这是子类
                resp = super().delete(ids=ids, customer_id=customer_id, db_session=db_session)
        if resp:
            db_session.commit()
        else:
            db_session.rollback()
        if need_close:
            db_session.close()
        return resp


class IoTEventOrm(MicServiceEventOrm):
    __tablename__ = 'iot_event'
    __table_args__ = (
        {"comment": "物联网(传感器)事件"}
    )
    id = Column(Integer, ForeignKey("mic_service_event.id"), primary_key=True, comment="主键")
    sensor_id = Column(Integer, nullable=True, default=None, comment="传感器对象ID")
    sensor_name = Column(String(64), nullable=True, default=None, comment="传感器名称")
    sensor_category = Column(String(64), nullable=True, default=None, comment="传感器类别")
    prev_status = Column(Integer, nullable=True, default=None, comment="传感器之前的状态 0: 正常,1: 离线,2: 值异常")
    sensor_status = Column(Integer, nullable=True, default=None, comment="传感器状态 0: 正常,1: 离线,2: 值异常")
    type = Column(String(64), nullable=True, default=None, comment="传感器事件类型 SensorEventType")

    __mapper_args__ = {
        'polymorphic_identity': EventType.IOT.value,
    }

    @classmethod
    def repair_type(cls) -> None:
        """
        修复传感器事件类型
        """
        db_session = new_db_session(sync=True)
        s = select(cls).where(cls.type.is_(None))
        for x in db_session.scalars(s):
            if "离线" in x.message:
                x.type = SensorEventType.OFFLINE.value
            else:
                x.type = SensorEventType.THRESHOLD.value
        db_session.commit()
        db_session.close()


class GatewayEventOrm(MicServiceEventOrm):
    __tablename__ = 'gateway_event'
    __table_args__ = (
        {"comment": "物联网消息"}
    )
    id = Column(Integer, ForeignKey("mic_service_event.id"), primary_key=True, comment="主键")
    # gateway_id = Column(Integer, nullable=True, default=None, comment="网关对象ID")
    gateway_status = Column(Integer, nullable=True, default=None, comment="网关状态 0: 正常,1: 离线,")
    gateway_ip = Column(String(63), nullable=True, default=None, comment="网关IP")
    trigger_name = Column(String(63), nullable=True, default=None, comment="触发器名称")
    trigger_type = Column(String(31), nullable=True, default=None, comment="触发器类型")
    trigger_source = Column(String(31), nullable=True, default=None, comment="触发器来源")
    # 定义鉴别器列
    __mapper_args__ = {
        'polymorphic_identity': EventType.GATEWAY.value,
    }
    # 定义一个用于join查询优化的索引,注意id放在列定义的最后。这是因为
    # id 放在最后是因为它不用于过滤，而是用于关联主表,和主表的索引衔接，放在这里可以覆盖索引（避免回表）
    # create index ix_gateway_event_quick_query on gateway_event (trigger_name, gateway_status, id);
    Index("ix_gateway_event_quick_query", 
          "trigger_name", 'gateway_status', 'id',
          )

class LanEventOrm(MicServiceEventOrm):
    __tablename__ = 'lan_event'
    __table_args__ = (
        {"comment": "内网事件"}
    )
    id = Column(Integer, ForeignKey("mic_service_event.id"), primary_key=True, comment="主键")
    node_id = Column(String(64), nullable=True, default=None, comment="内网节点对象ID")
    node_name = Column(String(64), nullable=True, default=None, comment="节点名称")
    device_type = Column(String(64), nullable=True, default=None, comment="节点设备类型")
    node_status = Column(Integer, nullable=True, default=None, comment="节点状态 0: 正常,1: 离线 和web-extend-api的定义正好相反")
    node_ip = Column(String(64), nullable=True, default=None, comment="节点IP")

    __mapper_args__ = {
        'polymorphic_identity': EventType.LAN.value,
    }


class PushTaskStatus(MyEnum):
    """
    * 等待: 初始状态
    * 队列中：放入异步队列进行处理的任务，包括立即执行的和短时间延时的任务
    * 取消: 源头事件恢复，任务被取消
    * 完成：任务执行完成， 注意，这不代表着所有的消息都发送成功。具体到每个消息，还要以 SendMessageRecordOrm 为准
    * 失败: 任务执行失败， 这通常意味着一个消息也没法送出去。
    """
    PENDING: str = "等待"
    IN_QUEUE: str = "队列中"
    CANCEL: str = "取消"
    COMPLETED: str = "完成"
    FAIL: str = "失败"


class OutPushTaskOrm(Base, OrmTools):
    __tablename__ = 'out_push_task'
    __table_args__ = (
        {"comment": "外推消息的任务"}
    )
    id = Column(Integer, primary_key=True, comment="主键")
    event_id = Column(Integer, nullable=True, default=None, comment="事件ID MicServiceEventOrm.id")
    customer_id = Column(Integer, nullable=True, default=None, comment="品牌客户ID")
    customer_name = Column(String(255), nullable=True, default=None, comment="品牌客户名称")
    store_id = Column(Integer, nullable=True, default=None, comment="门店ID")
    store_name = Column(String(128), nullable=True, default=None, comment="门店名称")
    raw_obj_id = Column(String(63), nullable=True, default=None, comment="原始对象id, 可能是内网节点id，zabbix host.id或者传感器id，为了兼容使用了字符串格式")
    raw_obj_status = Column(Integer, nullable=False, comment="RawObjectStatus.value")
    title = Column(String(64), nullable=True, default=None, comment="标题")
    content = Column(String(512), nullable=True, default=None, comment="内容")
    method = Column(String(64), nullable=True, default=None, comment="方法")
    targets = Column(ARRAY(String(128), dimensions=1), nullable=True, default=None, comment="目标") 
    status = Column(String(32), nullable=True, default=PushTaskStatus.PENDING.value, comment="状态")
    reason = Column(String(64), nullable=True, default=None, comment="取消原因")
    send_result = Column(JSON, nullable=True, default=None, comment="发送结果")
    is_recovery_task = Column(Integer, default=0, comment="是不是恢复事件的任务")
    alert_time =  Column(DateTime, nullable=True, default=None, comment="告警时间")
    plan_time = Column(DateTime, nullable=False, comment="计划时间")
    execute_time = Column(DateTime, nullable=True, default=None, comment="发送时间")
    create_time = Column(DateTime, nullable=True, default=datetime.now, comment="创建时间")
    update_time = Column(DateTime, nullable=True, onupdate=datetime.now, default=datetime.now, comment="更新时间")

    def get_kw(self) -> dict:
        """
        返回创建 OutPushTask 所需的参数
        """
        kw = self.to_dict()
        kw['mic_service_source'] = kw.pop('title')
        return kw
    
    @classmethod
    def mark_fail(cls, id: int, reason: str):
        """
        标记任务失败
        """
        db_session = new_db_session(sync=True)
        u = update(cls).where(cls.id == id).values(status=PushTaskStatus.FAIL.value, reason=reason)
        db_session.execute(u)
        db_session.commit()
    

class SendMessageRecordOrm(Base, OrmTools):
    __tablename__ = 'send_message_record'
    __table_args__ = (
        {"comment": "发送消息记录"}
    )
    id = Column(Integer, primary_key=True, comment="主键")
    task_id = Column(Integer, nullable=True, default=None, comment="任务ID OutPushTaskOrm.id")
    event_id = Column(Integer, nullable=False, comment="事件id MicServiceEventOrm.id")
    method = Column(String(64), nullable=True, default=None, comment="方法")
    target = Column(String(128), nullable=True, default=None, comment="目标")
    customer_id = Column(String(128), nullable=True, default=None, comment="品牌客户ID")
    customer_name = Column(String(255), nullable=True, default=None, comment="品牌客户名称")
    store_id = Column(Integer, nullable=True, default=None, comment="门店ID")
    store_name = Column(String(128), nullable=True, default=None, comment="门店名称")
    title = Column(String(64), nullable=True, default=None, comment="标题")
    content = Column(String(512), nullable=True, default=None, comment="内容")
    success = Column(Integer, nullable=True, default=0, comment="是否成功 1:成功 0:失败")
    send_result = Column(String(256), nullable=True, default=None, comment="发送结果")
    send_time = Column(DateTime, nullable=True, default=None, comment="发送时间")
    create_time = Column(DateTime, nullable=True, default=datetime.now, comment="创建时间")
    # 优化发送恢复消息时，查询历史发送记录的效率
    # CREATE INDEX idx_send_message_record_event_id ON send_message_record (event_id, success, send_time DESC)  INCLUDE (method, target); 
    Index('idx_send_message_record_event_id', event_id, success, send_time.desc() ,postgresql_include=['method', 'target'])

    @classmethod
    def batch_insert_from_task(cls, task: OutPushTaskOrm, db_session: Session):
        """
        根据任务批量插入发送记录
        注意，此函数没有执行数据库的提交
        :param task: _description_
        """
        for target in task.targets:
            send_result = task.send_result.get(target, None)
            if isinstance(send_result, str) and len(send_result) > 256:
                send_result = send_result[:256]
            success = 1 if send_result == 'success' else 0
            db_session.add(cls(task_id=task.id, 
                               event_id=task.event_id, 
                               method=task.method, 
                               target=target, 
                               customer_id=task.customer_id, 
                               customer_name=task.customer_name, 
                               store_id=task.store_id, 
                               store_name=task.store_name, 
                               title=task.title,
                               content=task.content,
                               success=success,
                               send_result=send_result, 
                               send_time=task.execute_time))


if __name__ == '__main__':
    pass